/*
 * Part of Snek vs Snacc webapp.
 * Released under AGPLv3. Copyright (C) NetherEran 2020
 * */

var field_width = 38
var field_height = 18

var pixel_width = 20

var wpixels = field_width * pixel_width
var hpixels = field_height * pixel_width

function isInsideMap(point) {
	return point.x >= 0 &&
			point.x < field_width &&
			point.y >= 0 &&
			point.y < field_height
}

var canvas = document.getElementById("game_field")
var context = canvas.getContext("2d")

function Point(x, y) {
	this.x = x
	this.y = y
	this.add = function(pt) {
		return new Point(this.x + pt.x, this.y + pt.y)
	}
	this.equals = function(pt) {
		return (this.x == pt.x && this.y == pt.y)
	}
	this.distance = function(pt) {
		return Math.sqrt((this.x - pt.x) ** 2 + (this.y - pt.y) ** 2)
	}
}

var player
var snacc
var points = 0
var game_going = false
var eatSnacc
var setLost








function Player(x, y, length) {
	this.positions = new Array()
	this.length = length
	for (var i = 0; i < length; i++) {
		this.positions[i] = new Point(x + i, y)
	}
	this.head_ptr = this.length - 1
	this.next_direction = new Point(1, 0)
	this.direction = this.next_direction
	this.move = function() {
		let oldhead = this.positions[this.head_ptr]
		this.head_ptr = (this.head_ptr + 1) % this.length
		this.direction = this.next_direction
		let newhead = oldhead.add(this.direction)
		
		if(newhead.equals(snacc.position)) {
			eatSnacc()
		}
		if (!isInsideMap(newhead)) {
			setLost()
		}
		for (var i = 0; i < this.length; i++) {
			if (i == this.head_ptr) {
				continue
			}
			if (this.positions[i].equals(newhead)) {
				setLost()
			}
		}
		
		this.positions[this.head_ptr] = newhead
	}
}

document.addEventListener('keydown', function(event) {
	if (!player) {
		return
	}
	switch(event.keyCode) {
		case 37: //LEFT
		case 65:
			if (player.direction.x != 1) {
				player.next_direction = new Point(-1, 0)
			}
			break
		case 38: //UP
		case 87:
			if (player.direction.y != 1) {
				player.next_direction = new Point(0, -1)
			}
			break
		case 39: //RIGHT
		case 68:
			if (player.direction.x != -1) {
				player.next_direction = new Point(1, 0)
			}
			break
		case 40: //DOWN	
		case 83:
			if (player.direction.y != -1) {
				player.next_direction = new Point(0, 1)
			}
			break
		default:
			break
	}
});

function Snacc(x, y, tier, direction) {
	this.position = new Point(x, y)
	this.tier = tier
	this.direction = direction
	this.move_tick = 0
	this.move = function() {
		//bounce
		if ((this.position.x + 1 == field_width && this.direction.x > 0) ||
			(this.position.x == 0 && this.direction.x < 0)) {
			this.direction.x = -this.direction.x
		}
		if ((this.position.y + 1 == field_height && this.direction.y > 0) ||
			(this.position.y == 0 && this.direction.y < 0)) {
			this.direction.y = -this.direction.y
		}
		//move
		this.position = this.position.add(this.direction)
		
		//check for collision with player
		for (var i = 0; i < player.length; i++) {
			if (player.positions[i].equals(this.position)) {
				if (i == player.head_ptr) {
					eatSnacc()
				} else {
					setLost()
				}
			}
		}
		if (this.direction == 2) {
			//alert()
		}
	}
	this.tick = function() {
		this.move_tick += this.tier ** 2
		if (this.move_tick > 17) {
			this.move()
			this.move_tick = 0
		}
	}
}

function addSnacc(max_value) {
	let tier = Math.floor(Math.random() * max_value) + 1
	let direction = Math.floor(Math.random() * 4)
	switch(direction) {
		case 0:
			direction = new Point(1, 1)
			break
		case 1:
			direction = new Point(-1, 1)
			break
		case 2:
			direction = new Point(-1, -1)
			break
		case 3:
			direction = new Point(1, -1)
			break
	}
	
	//find a point on the field that's far enough from the player to
	//not kill them right away
	let playerhead = player.positions[player.head_ptr]
	let spawnpoint = new Point(0, 0)
	do {
		spawnpoint.x = Math.floor(Math.random() * field_width)
		spawnpoint.y = Math.floor(Math.random() * field_height)
	} while (spawnpoint.distance(playerhead) < 13 + tier)
	
	snacc = new Snacc(spawnpoint.x, spawnpoint.y, tier, direction)
}


eatSnacc = function() {
	points += snacc.tier ** 2
	let maxval = Math.log(points) * 2 + 1
	addSnacc(maxval > 4 ? 4 : maxval)
}

setLost = function() {
	game_going = false
}


function drawPixel(point) {
	var posx = point.x * pixel_width
	var posy = point.y * pixel_width
	context.fillRect(posx, posy, pixel_width, pixel_width)
}

function getSnaccColor(tier) {
	switch(tier) {
		case 1:
			return "magenta"
		case 2:
			return "red"
		case 3:
			return "blue"
		case 4:
			return "yellow"
	}
}

//Graphics stuff
function redrawField() {
	context.clearRect(0, 0, canvas.width, canvas.height)
	
	let snacc_color = getSnaccColor(snacc.tier)
	context.fillStyle = snacc_color
	context.strokeStyle = snacc_color
	drawPixel(snacc.position)
	
	
	context.fillStyle = "cyan"
	context.strokeStyle = "cyan"
	for (var i = 0; i < player.length; i++) {
		drawPixel(player.positions[i])
	}
}

function drawGameOverScreen(points) {
	
	context.clearRect(0, 0, canvas.width, canvas.height)
	context.fillStyle = "lightgrey"
	context.strokeStyle = "lightgrey"
	context.font = "15pt a"
	context.fillText("Game Over", 320, 200)
	context.fillText(points + " Points", 320, 230)
}


//Game loop stuff
var now, dt, last = timestamp();

function timestamp() {
	return window.performance && window.performance.now ? window.performance.now() : new Date().getTime();
}

var player_tick = 0
var snacc_tick = 0

function tick() {
	now = timestamp()
	dtime = (now - last) / 1000 //in seconds
	last = now
	
	let redraw = false
	
	player_tick += dtime
	if (player_tick >= 0.15) {
		player.move()
		redraw = true
		player_tick -= 0.15
	}
	snacc_tick += dtime
	if (snacc_tick >= 0.05) {
		snacc.tick()
		redraw = true
		snacc_tick -= 0.05
	}
	
	if (game_going) {
		if (redraw) {
			redrawField()
		}
		requestAnimationFrame(tick);
	} else {
		drawGameOverScreen(points)
	}
}



function start() {
	//replace start button with restart button
	var startbtn = document.getElementById("start_button")
	startbtn.innerHTML = "RESTART"
	
	//start game
	player = new Player(Math.floor(field_width / 2), Math.floor(field_height / 2), 6)
	addSnacc(1)
	points = 0
	player_tick = 0
	snacc_tick = 0
	now   = timestamp()
	last = now
	game_going = true
	redrawField()
	requestAnimationFrame(tick);
}
